package icu.lookyousmileface.netty.simple.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author shenj
 * @title: GroupChatServer
 * @projectName NettyPro
 * @date 2020/12/7 13:44
 */
@SuppressWarnings("ALL")
public class GroupChatServer {
    /**
     * @author shenj
     * @title: variable
     * @date 2020/12/7  14:29
     * 注意：channelGroup用于存储handlerAdded中的客户端的channel
     */
    private int port;
    private static final EventLoopGroup mainLoop = new NioEventLoopGroup();
    private static final EventLoopGroup workerLoop = new NioEventLoopGroup();
    private static final ServerBootstrap startApplicaiontConfiguration = new ServerBootstrap();
    private static ChannelFuture channelFuture = null;
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * @author shenj
     * @title: GroupChatServer
     * @date 2020/12/7  14:32
     * constructor
     */
    public GroupChatServer(@NotNull int portIn) {
        this.port = portIn;
    }

    /**
     * @author shenj
     * @title: run
     * @date 2020/12/7  14:33
     * body
     */
    public void run() throws InterruptedException {

        try {
            //配置channel、option、chilHandler
            startApplicaiontConfiguration.group(mainLoop, workerLoop)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

                            ChannelPipeline pipeline = ch.pipeline();
                            //加入编解码器
                            pipeline.addLast("stringDecoder", new StringDecoder());
                            pipeline.addLast("stringEncoder", new StringEncoder());
                            //SimpleChannelInboundHandler<I>中的I表示发送数据类型【需要记忆】
                            pipeline.addLast("ServerHandler", new SimpleChannelInboundHandler<String>() {
                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                                /**
                                 * @author shenj
                                 * @title: handlerAdded
                                 * @date 2020/12/7  14:17
                                 * 客户一旦加入就执行的的方法
                                 */
                                @Override
                                public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

                                    Channel channel = ctx.channel();
                                    //先发送再加入自己加入组，就可以避免发送给自己
                                    channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 加入聊天" + sdf.format(new Date()) + " \n");
                                    channelGroup.add(channel);

                                }

                                /**
                                 * @author shenj
                                 * @title: channelActive
                                 * @date 2020/12/7  14:38
                                 * 通道被激活表示上线了,在服务端显示
                                 */
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {

                                    System.out.println("[客户端]" + ctx.channel().remoteAddress() + " 上线了~");
                                }

                                /**
                                 * @author shenj
                                 * @title: channelInactive
                                 * @date 2020/12/7  14:48
                                 * channel不活跃、在服务端显示离线
                                 */
                                @Override
                                public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                                    System.out.println("[客户端]" + ctx.channel().remoteAddress() + " 离线了~");
                                }

                                /**
                                 * @author shenj
                                 * @title: handlerRemoved
                                 * @date 2020/12/7  14:50
                                 * 发送给其他在线的所有客户端，当handlerRemoved触发将会从channleGroup的移除
                                 */
                                @Override
                                public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

                                    channelGroup.writeAndFlush("[客户端]" + ctx.channel().remoteAddress() + " 离开了~");
                                }

                                /**
                                 * @author shenj
                                 * @title: channelRead0
                                 * @date 2020/12/7  14:18
                                 * 读取方法
                                 */
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

                                    Channel channel = ctx.channel();
                                    channelGroup.forEach(customer -> {
                                        customer.writeAndFlush(customer != channel ? "[客户端]" + customer.remoteAddress() + "发送了消息:" + msg + "\n" : "[自己]发送发送了消息:" + msg + "\n");
                                    });

                                }

                                /**
                                 * @author shenj
                                 * @title: exceptionCaught
                                 * @date 2020/12/7  14:44
                                 * 异常处理
                                 */
                                @Override
                                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

                                    ctx.close();
                                }
                            });
                        }
                    });

            System.out.println("Server is runing ~");

            channelFuture = startApplicaiontConfiguration.bind(port).sync();

            channelFuture.channel().closeFuture().sync();
        } finally {
            mainLoop.shutdownGracefully();
            workerLoop.shutdownGracefully();
        }

    }

    public static void main(String[] args) throws Exception {

        new GroupChatServer(8881).run();
    }
}
